package p2p.tracker.message;

import java.awt.List;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;

import p2p.tracker.client.Client;
import p2p.tracker.client.ClientManager;
import p2p.utils.Utils;

/**
 * 
 * @author p2pGroup
 * 
 * Message Class represents one Message in the p2p - Protokoll. In the following the 
 * architektur of one message is given 
 * 
 * ----------------------------------------------------------------------------------
 * |0 - 15 = UUID | 16 = Command | 17 = ack | 18 - 19 = sqn | 20 - 21 = tn| paylood  | = 1024 bytes
 * ----------------------------------------------------------------------------------
 */
public class Message implements Command{
	
	enum Command{
		connect , // 0
		spawn , // 1
		leave , // 2
	}
	
	private InetAddress requestAddress;
	private UUID uuid;
	private int sequenceNumber;
	private int totalNumber;
	private int command;
	private boolean ack;
	private byte[] data;
	private String clientIp;
	private int clientPort;
	
	public Message(DatagramPacket packet, byte[] recieveData){
	
		this.requestAddress = packet.getAddress();
		this.uuid = Utils.toUUID(Arrays.copyOfRange(recieveData, 0, 16));
		this.command = Integer.valueOf(recieveData[16]);
		this.ack = recieveData[17] > 0 ? true : false;
		byte [] ipInfo = new byte [4];
		ipInfo[0] = recieveData[22];
		ipInfo[1] = recieveData[23];
		ipInfo[2] = recieveData[24];
		ipInfo[3] = recieveData[25];
		
		this.clientIp = Utils.getIpFromBytesArray(ipInfo);
		byte [] portInfo = new byte[4];
		portInfo[0] = recieveData[26];
		portInfo[1] = recieveData[27];
		portInfo[2] = recieveData[28];
		portInfo[3] = recieveData[29];
		this.clientPort = Utils.byteArrayToInt(portInfo);
		
		System.out.println("Remote PORT " + packet.getPort());		
		System.out.println("Remote IP " + packet.getAddress());			
		System.out.println("CLIENT PORT " + this.clientPort);		
		System.out.println("CLIENT ip " + this.clientIp);
		
		System.out.println("[14] : " + recieveData[14]);
		System.out.println("[15] : " + recieveData[15]);
		System.out.println("[16] : " + recieveData[16]);
		System.out.println("[17] : " + recieveData[17]);
		System.out.println("[18] : " + recieveData[18]);
		System.out.println("[19] : " + recieveData[19]);
		System.out.println("[20] : " + recieveData[20]);
		System.out.println("[21] : " + recieveData[21]);
		System.out.println("[22] : " + recieveData[22]);
		System.out.println("[23] : " + recieveData[23]);
		System.out.println("[24] : " + recieveData[24]);
		System.out.println("[25] : " + recieveData[25]);
		System.out.println("[26] : " + recieveData[26]);
		System.out.println("[27] : " + recieveData[27]);
		System.out.println("[28] : " + recieveData[28]);
		System.out.println("[29] : " + recieveData[29]);
		
		//this.sequenceNumber = Integer.valueOf(new String(recieveData,17,20));
		//this.totalNumber = Integer.valueOf(new String(recieveData,20,23));
		//this.ack = recieveData[23] > 0 ? true : false;
		//this.data = Arrays.copyOfRange(recieveData, 24, 1024);	
	}
	
	public Message(UUID uuid, boolean isAck){
		this.ack = isAck;
		this.uuid = uuid;
	}
	
	@Override
	public void execute() {
		
		System.out.println("BEFORE EXECUTE ORDINAL !!!");
		System.out.println("COMMAND: " + this.command);
		System.out.println("COMMAND: " + this.command);
		System.out.println("COMMAND: " + this.command);
		System.out.println("SPAWN ORDINAL: " + Message.Command.spawn.ordinal());
		System.out.println("CONNECT ORDINAL: " + Message.Command.connect.ordinal());
		System.out.println("LEAVE ORDINAL: " + Message.Command.leave.ordinal());
		System.out.println("------------------------------------");
		
		if (Message.Command.connect.ordinal() == this.command){
			
			//TODO Implement when message is Connect
			System.out.println("Inside CONNECT ORDINAL !!!");
			ClientManager.getInstance().connected(uuid, null);			
		}
		
		if (Message.Command.spawn.ordinal() == this.command){
			System.out.println("Inside SPAWN ORDINAL !!!");
			//TODO Is this really ok - to have to request Address
			Client a = new Client(this.requestAddress);
			Client b = new Client(this.requestAddress);
			
			ClientManager.getInstance().spawn(this.uuid, a, b);
			
		}
		
		if (Message.Command.leave.ordinal() == this.command){
			//TODO Implement when message is Leave
			System.out.println("Inside LEAVE ORDINAL !!!");
		}
		
		System.out.println("AFTER  EXECUTE ORDINAL !!!");		
	}

	public int getSequenceNumber() {
		return sequenceNumber;
	}

	public void setSequenceNumber(int sequenceNumber) {
		this.sequenceNumber = sequenceNumber;
	}

	public int getTotalNumber() {
		return totalNumber;
	}

	public void setTotalNumber(int totalNumber) {
		this.totalNumber = totalNumber;
	}

	public boolean isAck() {
		return ack;
	}

	public void setAck(boolean ack) {
		this.ack = ack;
	}

	public byte[] getData() {
		return data;
	}

	public void setData(byte[] data) {
		this.data = data;
	}

	public UUID getUuid() {
		return uuid;
	}

	public InetAddress getRequestAddress() {
		return requestAddress;
	}
	
	public String toString(){
		return "UUID: " + uuid.toString() + " Command " + this.command;
	}
	
	public int getClientPort(){
		return clientPort;
	}
	
	public String getClientIp() {
		return clientIp;
	}
}

